/*
 * Copyright 1993-2010 NVIDIA Corporation.  All rights reserved.
 *
 * Please refer to the NVIDIA end user license agreement (EULA) associated
 * with this source code for terms and conditions that govern your use of
 * this software. Any use, reproduction, disclosure, or distribution of
 * this software and related documentation outside the terms of the EULA
 * is strictly prohibited.
 *
 */



#include "multithreading.h"

#if _WIN32
    //Create thread
    CUTThread cutStartThread(CUT_THREADROUTINE func, void *data){
        return CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, data, 0, NULL);
    }

    //Wait for thread to finish
    void cutEndThread(CUTThread thread){
        WaitForSingleObject(thread, INFINITE);
        CloseHandle(thread);
    }

    //Destroy thread
    void cutDestroyThread(CUTThread thread){
        TerminateThread(thread, 0);
        CloseHandle(thread);
    }

    //Wait for multiple threads
    void cutWaitForThreads(const CUTThread * threads, int num){
        WaitForMultipleObjects(num, threads, true, INFINITE);

        for(int i = 0; i < num; i++)
            CloseHandle(threads[i]);
    }

	//Create barrier.
	CUTBarrier cutCreateBarrier(int releaseCount) {
		CUTBarrier barrier;

		InitializeCriticalSection(&barrier.criticalSection);
		barrier.barrierEvent = CreateEvent(NULL, TRUE, FALSE, TEXT("BarrierEvent"));
		barrier.count = 0;
		barrier.releaseCount = releaseCount;

		return barrier;
	}

	//Increment barrier. (excution continues)
	void cutIncrementBarrier(CUTBarrier* barrier) {
		int myBarrierCount;
		EnterCriticalSection(&barrier->criticalSection);
		myBarrierCount = ++barrier->count;
		LeaveCriticalSection(&barrier->criticalSection);

		if( myBarrierCount >= barrier->releaseCount ) {
			SetEvent(barrier->barrierEvent);
		}
	}

	//Wait for barrier release.
	void cutWaitForBarrier(CUTBarrier* barrier) {
		WaitForSingleObject(barrier->barrierEvent, INFINITE);
	}

	//Destory barrier
	void cutDestroyBarrier(CUTBarrier* barrier) {
		
	}


#else
    //Create thread
    CUTThread cutStartThread(CUT_THREADROUTINE func, void * data){
        pthread_t thread;
        pthread_create(&thread, NULL, func, data);
        return thread;
    }

    //Wait for thread to finish
    void cutEndThread(CUTThread thread){
        pthread_join(thread, NULL);
    }

    //Destroy thread
    void cutDestroyThread(CUTThread thread){
        pthread_cancel(thread);
    }

    //Wait for multiple threads
    void cutWaitForThreads(const CUTThread * threads, int num){
        for(int i = 0; i < num; i++)
            cutEndThread(threads[i]);
    }

	//Create barrier.
	CUTBarrier cutCreateBarrier(int releaseCount) {
		CUTBarrier barrier;

		barrier.count = 0;
		barrier.releaseCount = releaseCount;

		pthread_mutex_init(&barrier.mutex, 0);
		pthread_cond_init(&barrier.conditionVariable,0);


		return barrier;
	}

	//Increment barrier. (excution continues)
	void cutIncrementBarrier(CUTBarrier* barrier) {
		int myBarrierCount;
		pthread_mutex_lock(&barrier->mutex);
		myBarrierCount = ++barrier->count;
		pthread_mutex_unlock(&barrier->mutex);
	
		if( myBarrierCount >=barrier->releaseCount ) {
			pthread_cond_signal(&barrier->conditionVariable);
		}
	}

	//Wait for barrier release.
	void cutWaitForBarrier(CUTBarrier* barrier) {
		pthread_mutex_lock(&barrier->mutex);
		while(barrier->count < barrier->releaseCount)
		  pthread_cond_wait(&barrier->conditionVariable, &barrier->mutex);
		pthread_mutex_unlock(&barrier->mutex);
	}

	//Destory barrier
void cutDestroyBarrier(CUTBarrier* barrier)
	{
	  pthread_mutex_destroy(&barrier->mutex);
	  pthread_cond_destroy(&barrier->conditionVariable);
	}

#endif
